package com.laviedesfons.tinapoc.ant;

import com.laviedesfons.tinapoc.dejasmin;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.bcel.classfile.ClassFormatException;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import tina.utils.NeoJasminVisitor;

/**
 *
 * @author Romano
 */
public class DeJasminTask extends Task{


    private boolean printoffsets = false,   // print offsets (both modes)
                    oldjasmin = false,     // target jasmin v1.1
                    warmode = false,       // don't parse sequentially
                    nolabels = false;   
    
    private String jarfile;
    private String outpath;
    
    public void setWarmode(Boolean warmode){
        this.warmode = warmode.booleanValue();
    }
    
    public void setPrintoffsets(Boolean printoffsets){
        this.printoffsets = printoffsets.booleanValue();
    }
    
    public void setOldjasmin(Boolean oldjasmin){
        this.oldjasmin = oldjasmin.booleanValue();
    }
    
    public void setNolabels(Boolean nolabels){
        this.nolabels = nolabels.booleanValue();
    }    
    
    public void setJArfile(String jarfile){
        this.jarfile = jarfile;
    }

    public void setOutpath(String location) {
        this.outpath = location;
    } 
    
    @Override
    public void execute(){
        try {          
            if(jarfile == null)
                throw new BuildException("missing required property: jarfile");
            if(outpath == null)
                throw new BuildException("missing required property: outpath");
            File tmpJarFile = new File(jarfile);
            if(!(tmpJarFile.exists() && tmpJarFile.canRead()))
                throw new BuildException("unable to read jarfile: "+jarfile);
            File parent = new File(outpath);
            if(!(parent.exists() && parent.isDirectory() && parent.canWrite()))
                throw new BuildException("unable to write in directory: "+outpath);  
            
            File warmodeTmp = null;
            if(warmode == true){
                Logger.getLogger(DeJasminTask.class.getName()).log(Level.INFO,
                    "warmode ON");
                File sysTempDir = new File(System.getProperty("java.io.tmpdir"));
                if(sysTempDir.exists() &&
                        sysTempDir.isDirectory() && sysTempDir.canWrite()){
                    warmodeTmp = new File(sysTempDir,"dejasmintask.d/");
                    warmodeTmp.mkdirs();
                    warmodeTmp.deleteOnExit();
                } else
                    throw new BuildException("unable to create temp dir");
            }
            
            JarFile jarFile = new JarFile(jarfile);
            // getAllClassFilesPath
            ArrayList<JarEntry> classFiles = new ArrayList<JarEntry>();
            Enumeration<JarEntry> jarEntries = jarFile.entries();
            while (jarEntries.hasMoreElements()) {
                JarEntry entry = jarEntries.nextElement();
                String cName = entry.getName();
                if (cName.endsWith(".class")
                        && cName.indexOf(".") == cName.lastIndexOf(".")) {
                    classFiles.add(entry);
                    if (warmode) {
                        InputStream is;
                        try {
                            File fsClassPath = new File(warmodeTmp,
                                cName.substring(0, cName.lastIndexOf("/")));
                            fsClassPath.mkdirs();
                            fsClassPath.deleteOnExit();
                            is = jarFile.getInputStream(entry);
                            File tmpFile = new File(warmodeTmp, cName);
                            tmpFile.deleteOnExit();
                            OutputStream out = new FileOutputStream(tmpFile);
                            byte buf[] = new byte[1024];
                            int len;
                            while ((len = is.read(buf)) > 0)
                                out.write(buf, 0, len);                            
                            out.close();
                            is.close();
                        } catch (IOException ex) {
                            Logger.getLogger(DeJasminTask.class.getName()).log(Level.SEVERE,
                                    "error extracting", ex);
                        }
                    }
                }
            }
            
            for(JarEntry entry : classFiles){
                String className = entry.getName();
                Logger.getLogger(DeJasminTask.class.getName()).log(Level.INFO,
                        "desassembling: {0}", className);
                JavaClass java_class = 
                        new ClassParser(jarFile.getInputStream(entry),className).parse();
                File fsClassPath = new File(parent,
                        className.substring(0, className.lastIndexOf("/")));
                fsClassPath.mkdirs();
                FileOutputStream fos = 
                        new FileOutputStream(new File(parent,className.replace(".class", ".j")));
                if(warmode){
                    String cls = new File(warmodeTmp,className).getAbsolutePath();
                    new dejasmin().warDisassemble(cls, fos);
                }
                else
                    new NeoJasminVisitor(java_class,fos,
                        oldjasmin, nolabels, printoffsets).disassemble();
            }
        } catch (ClassFormatException ex) {
            throw new BuildException("can't produce Jasmin output: "+
                    ex.getMessage(), ex);
        } catch (FileNotFoundException ex) {
            throw new BuildException("can't produce Jasmin output: "+
                    ex.getMessage(), ex);
        } catch (IOException ex) {
            throw new BuildException("can't produce Jasmin output: "+
                    ex.getMessage(), ex);
        }
    }
}